package com.tian.arch.function.adapter.core

import android.content.Context
import androidx.annotation.IntRange
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.tian.arch.function.adapter.ViewHolder
import com.tian.arch.function.adapter.diff.LiteDiffUtil
import com.tian.arch.function.adapter.diff.LiteListUpdateCallback
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext


/**
 * 数据操作接口
 */
interface IDataOperator<D> {
    fun getItem(index: Int): D
    fun addData(item: D)
    fun addData(index: Int, item: D)
    fun addAll(items: List<D>)
    fun addAll(index: Int, items: List<D>)
    fun remove(index: Int)
    fun modify(index: Int, newData: D)
    fun modify(index: Int, action: (D) -> Unit)
    fun refreshData(items: List<D>)
    fun clear()
}

typealias OnItemClickListener<T> = (index: Int, item: T) -> Unit
typealias OnItemLongClickListener<T> = (index: Int, item: T) -> Unit

/**
 *  adapter基类，主要定义数据集 和如何操作数据
 */
abstract class AbsAdapter<T>(private val context: Context) : RecyclerView.Adapter<ViewHolder>(), IDataOperator<T> {

    protected var diffCallback: LiteDiffUtil.Callback? = null
    protected var dataSet = mutableListOf<T>()

    // hook 预留
    open fun beforeUpdateData() {}
    open fun afterUpdateData() {}

    open fun notifyIndex(index: Int): Int {
        return index
    }

    override fun getItem(index: Int) = dataSet[index]

    /**
     * 添加数据时，操作得到一个新增的"index"，然后调用[notifyItemInserted]进行更新
     */
    override fun addData(item: T) {
        val notifyIndex = notifyIndex(dataSet.size)
        dataSet.add(item)
        notifyItemInserted(notifyIndex)
    }

    override fun addData(index: Int, item: T) {
        dataSet.add(index, item)
        notifyItemInserted(notifyIndex(index))
    }

    override fun addAll(items: List<T>) {
        if (items.isNotEmpty()) {
            val startIndex = notifyIndex(dataSet.size)
            dataSet.addAll(items)
            notifyItemRangeInserted(startIndex, items.size)
        }
    }

    override fun addAll(@IntRange(from = 0) index: Int, items: List<T>) {
        if (items.isNotEmpty()) {
            dataSet.addAll(index, items)
            notifyItemRangeInserted(notifyIndex(index), items.size)
        }
    }

    override fun remove(@IntRange(from = 0) index: Int) {
        dataSet.removeAt(index)
        if (dataSet.isEmpty()) {
            notifyDataSetChanged()
        } else {
            notifyItemRemoved(index)
        }
    }

    override fun modify(@IntRange(from = 0) index: Int, newData: T) {
        dataSet[index] = newData
        notifyItemChanged(index)
    }

    override fun modify(@IntRange(from = 0) index: Int, action: (T) -> Unit) {
        action(dataSet[index])
        notifyItemChanged(index)
    }

    override fun refreshData(items: List<T>) {
        if (diffCallback != null && dataSet.isNotEmpty()) {

            val diffResult = DiffUtil.calculateDiff(LiteDiffUtil(dataSet, items, diffCallback!!))
            dataSet = ArrayList(items)
            beforeUpdateData()
            diffResult.dispatchUpdatesTo(LiteListUpdateCallback(this@AbsAdapter))
            afterUpdateData()

//            (context as LifecycleOwner).lifecycleScope.launch(Dispatchers.IO) {
//                val diffResult = DiffUtil.calculateDiff(LiteDiffUtil(dataSet, items, diffCallback!!))
//                withContext(Dispatchers.Main) {
//                    dataSet = ArrayList(items)
//                    beforeUpdateData()
//                    diffResult.dispatchUpdatesTo(LiteListUpdateCallback(this@AbsAdapter))
//                    afterUpdateData()
//                }
//            }
        } else {
            dataSet.clear()
            dataSet.addAll(items)
            beforeUpdateData()
            notifyDataSetChanged()
            afterUpdateData()
        }
    }

    override fun clear() {
        dataSet.clear()
        notifyDataSetChanged()
    }
}